/**
 * @Author       : oak-c
 * @Date         : 2021-01-18 16:17:30
 * @Description  :
 * @LastEditors  : oak-c
 * @LastEditTime : 2021-01-21 18:26:00
 * @FilePath     : /src/modules/logger/index.ts
 */
import { LoggerService, Injectable, ArgumentsHost } from '@nestjs/common'
import { ConfigService } from '@donews/nestjs-config'
import { configure, getLogger, Logger } from 'log4js'
import Path from 'path'
import { omit } from 'lodash'

@Injectable()
export class CustomLoggerService implements LoggerService {
    constructor(private readonly config: ConfigService) {
        this.requestLogger = getLogger('request')
        this.errorLogger = getLogger('error')
        const firstDir = Path.resolve('logs')
        const loggerDir = `${firstDir}/${config.get('service.name', 'localhost')}`
        const layout = {
            type: 'pattern',
            pattern: '[%d{yyyy-MM-dd hh:mm:ss}] [%p] %h %z %m',
        }
        configure({
            appenders: {
                console: {
                    type: 'console',
                    layout,
                },
                stdout: {
                    type: 'dateFile',
                    filename: `${loggerDir}/log.log`,
                    alwaysIncludePattern: true,
                    keepFileExt: true,
                    layout,
                },
            },
            categories: {
                default: { appenders: ['console'], level: 'all' },
                request: { appenders: ['console', 'stdout'], level: 'info' },
                error: { appenders: ['console', 'stdout'], level: 'warn' },
            },
        })
    }

    private readonly requestLogger: Logger
    private readonly errorLogger: Logger

    log(message: string, ...args: any[]): void {
        this.info(message, ...args)
    }

    info(message: string, ...args: any[]): void {
        this.requestLogger.info(message, ...args)
    }

    requestError(message: string, ...args: any[]): void {
        this.requestLogger.error(message, ...args)
    }

    warn(message: string, ...args: any[]): void {
        this.errorLogger.warn(message, ...args)
    }

    error(message: string, ...args: any[]): void {
        this.errorLogger.error(message, ...args)
    }

    filterLog(context: ArgumentsHost, exception?: any, message?: string, data?: any) {
        const ctx = context.switchToHttp()
        const request = ctx.getRequest()
        const { url, originalUrl, headers, method, body } = request
        const ua = headers['user-agent']
        const user = request?.user?.userinfo?.userid || request?.user?.userid
        const tags = ['[%s]', '[%s]', '[%s]']
        const logs = [method, url !== '/' ? url : originalUrl, ua]
        if (user) {
            tags.push('U:%s')
            logs.push(user)
        }
        if (typeof body === 'object' && body) {
            const reqStr = JSON.stringify(omit(body, ['__user']))
            tags.push('I:%s')
            if (reqStr.length > 1000) {
                logs.push(`入参超长：${reqStr.slice(0, 1000)}...`)
            } else {
                logs.push(reqStr)
            }
        }
        if (data) {
            try {
                const datastr = JSON.stringify(data)
                tags.push('O:%s')
                if (datastr.length > 3000) {
                    logs.push('响应体超长')
                } else {
                    logs.push(datastr)
                }
            } catch (e) {}
        }
        if (message) {
            tags.push('M:%s')
            logs.push(message)
        }
        this.info(tags.join(' '), ...logs)
        if (exception) {
            this.error(exception)
        }
    }
}
